package com.aim.analysis;

import com.aim.analysis.records.Game;
import com.aim.analysis.records.GameMap;
import com.aim.analysis.records.Hit;
import com.aim.analysis.records.Scenario;
import com.aim.azure.AzureConnection;

import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * User: Avraham Shvartzon
 * Date: 6/22/14
 */
public class BonusCalculator{

    private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private static final HashMap<String, AbstractPaymentPolicy> paymentMap = new HashMap<String, AbstractPaymentPolicy>();
    static{
        AllGamePolicy p_2014_06_16 = new AllGamePolicy("20140616", 0.2);
        p_2014_06_16.addBonusLevel(500, 0);
        p_2014_06_16.addBonusLevel(1000, 0.25);
        p_2014_06_16.addBonusLevel(1500, 0.5);
        p_2014_06_16.addBonusLevel(2000, 0.75);
        p_2014_06_16.addBonusLevel(2001, 1);
        paymentMap.put(p_2014_06_16.date, p_2014_06_16);

        AllGamePolicy p_2014_06_19 = new AllGamePolicy("20140619", 0.2);
        p_2014_06_19.addBonusLevel(1000, 0);
        p_2014_06_19.addBonusLevel(3000, 0.25);
        p_2014_06_19.addBonusLevel(4500, 0.5);
        p_2014_06_19.addBonusLevel(6000, 0.75);
        p_2014_06_19.addBonusLevel(6001, 1);

        AllGamePolicy p_2014_07_14 = new AllGamePolicy(p_2014_06_19, "20140714");
        paymentMap.put(p_2014_07_14.date, p_2014_07_14);

        AllGamePolicy p_2014_07_24 = new AllGamePolicy(p_2014_06_19, "20140724");
        paymentMap.put(p_2014_07_24.date, p_2014_07_24);

        AllGamePolicy p_2014_07_31 = new AllGamePolicy(p_2014_06_19, "20140731");
        paymentMap.put(p_2014_07_31.date, p_2014_07_31);

        AllGamePolicy p_2014_08_21 = new AllGamePolicy(p_2014_06_19, "20140821");
        paymentMap.put(p_2014_08_21.date, p_2014_08_21);

        BestGamePolicy bestGamePolicy = new BestGamePolicy("20150115", 0.2);
        bestGamePolicy.addBonusLevel(2000, 0);
        bestGamePolicy.addBonusLevel(3000, 0.3);
        bestGamePolicy.addBonusLevel(5000, 0.6);
        bestGamePolicy.addBonusLevel(7000, 1);
        bestGamePolicy.addBonusLevel(10000, 1.5);
        bestGamePolicy.addBonusLevel(10001, 2);
        paymentMap.put(bestGamePolicy.date, bestGamePolicy);

    }
    public static void main(String[] args) throws URISyntaxException, InvalidKeyException {
        Scenario.register(Scenario.fgnScenario);
        AzureConnection azureConnection = new AzureConnection();
        azureConnection.connect();
        String experimentDate = "20150115";
        azureConnection.addExperimentDate(experimentDate);

        GameMap gameMap = new GameMap();
        List<Hit> hits = Hit.createHits(gameMap, azureConnection);

        System.out.println("Table Date"+ "\t" + "Game Date" + "\t" + "workerId" + "\t" + "assignmentId" + "\t" + "payment");
        for (Hit hit : hits) {

            System.out.println(experimentDate + "\t" + dateFormat.format(hit.getDate()) + "\t" + hit.workerId + "\t" + hit.assignmentId + "\t" + calculatePayment(hit, experimentDate));
        }

    }



    public static double calculatePayment(Hit hit, String date){
        AbstractPaymentPolicy paymentPolicy = paymentMap.get(date);
        return paymentPolicy.calculatePayment(hit);
    }

    private static abstract class AbstractPaymentPolicy {

        final String date;
        final double basePayment;
        TreeMap<Double, Double> bonusTree = new TreeMap<Double, Double>();

        private AbstractPaymentPolicy(String date, double basePayment) {
            this.date = date;
            this.basePayment = basePayment;
        }

        private AbstractPaymentPolicy(AllGamePolicy paymentPolicy, String date){
            this.date = date;
            this.basePayment = paymentPolicy.basePayment;
            this.bonusTree = new TreeMap<Double, Double>(paymentPolicy.bonusTree);
        }

        public void addBonusLevel(double upToScores, double bonus){
            bonusTree.put(upToScores, bonus);
        }

        public double getBonus(double scores){
            Map.Entry<Double, Double> entry = bonusTree.higherEntry(scores);
            if (entry == null){
                entry = bonusTree.lastEntry();
            }
            return entry.getValue();
        }

        public abstract double calculatePayment(Hit hit);
    }

    private static class AllGamePolicy extends AbstractPaymentPolicy {

        public AllGamePolicy(String date, double basePayment) {
            super(date, basePayment);
        }

        public AllGamePolicy(AllGamePolicy policy, String date) {
            super(policy, date);
        }

        public double calculatePayment(Hit hit) {
            int totalScores = 0;
            for (Game game : hit.gameList) {
                totalScores += game.getFinalScore();
            }
            return basePayment + getBonus(totalScores);
        }
    }

    private static class BestGamePolicy extends AbstractPaymentPolicy {


        private BestGamePolicy(String date, double basePayment) {
            super(date, basePayment);
        }

        @Override
        public double calculatePayment(Hit hit) {
            List<Game> games = hit.gameList;
            int firstGameScores = games.get(0).calculatedScore;
            int bestGame = 0;
            for (int i = 2; i < games.size(); i++) {
               bestGame = Math.max(bestGame, games.get(i).calculatedScore);
            }
            return basePayment + getBonus(firstGameScores + bestGame);
        }
    }




}
